07. Exercise: Draw the Fan Controller

21 5 AAK Draw Custom View Slides-SC

Android Developer Documentation

Exercise

In this exercise you are going to draw the fan controller custom view onto the screen.

  1. In the DialView class, below the initializations, override the [onSizeChanged()](https://developer.android.com/reference/android/view/View.html#onSizeChanged%28int, int, int, int%29) method from the View class to calculate the size for the custom view's dial. Import kotlin.math.min when requested.
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
   radius = (min(width, height) / 2.0 * 0.8).toFloat()
}
  1. Below onSizeChanged() add this code to define a computeXYForSpeed() extension function for the PointF class.

  2. Import kotlin.math.cos and kotlin.math.sin when requested. This extension function on the PointF class calculates the X, Y coordinates on the screen for the text label and current indicator (0, 1, 2, or 3), given the current FanSpeed position and radius of the dial. You'll use this in onDraw().

private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
   // Angles are in radians.
   val startAngle = Math.PI * (9 / 8.0)   
   val angle = startAngle + pos.ordinal * (Math.PI / 4)
   x = (radius * cos(angle)).toFloat() + width / 2
   y = (radius * sin(angle)).toFloat() + height / 2
}
  1. Override the onDraw() method to render the view on the screen with the Canvas and Paint classes. Import android.graphics.Canvas when requested. This is the skeleton override:
override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)

}
  1. Inside onDraw() add this line to set the paint color to gray (Color.GRAY) or green (Color.GREEN) depending on whether the fan speed is OFF or any other value. Import android.graphics.Color when requested.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
  1. Add this code to draw a circle for the dial, with the [drawCircle()](https://developer.android.com/reference/android/graphics/Canvas.html#drawCircle%28float, float, float, android.graphics.Paint%29) method.
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
  • This method uses the current view width and height to find the center of the circle, the radius of the circle, and the current paint color.
  • The width and height properties are members of the View superclass and indicate the current dimensions of the view.
  1. Add this code to draw a smaller circle for the fan speed indicator mark, also with the drawCircle() method.
    • This part uses the PointF.computeXYforSpeed() extension method to calculate the X,Y coordinates for the indicator center based on the current fan speed.
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
  1. Finally, draw the fan speed labels (0, 1, 2, 3) at the appropriate positions around the dial. This part of the method calls PointF.computeXYForSpeed() again to get the position for each label, and reuses the pointPosition object each time to avoid allocations. Use [drawText()](https://developer.android.com/reference/android/graphics/Canvas.html#drawText%28java.lang.String, int, int, float, float, android.graphics.Paint%29) to draw the labels.
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
   pointPosition.computeXYForSpeed(i, labelRadius)
   val label = resources.getString(i.label)
   canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}
  1. Open activity_main.xml.

To add a custom view to an app's UI, you specify it as an element in the activity's XML layout. Control its appearance and behavior with XML element attributes, as you would for any other UI element.

  1. In activity_main.xml change the ImageView tag for the dialView to com.example.android.customfancontroller.DialView

  2. Delete the android:background attribute.

Both DialView and the original ImageView inherit the standard attributes from the View class, so there is no need to change any of the other attributes. The new DialView element looks like this:

<com.example.android.customfancontroller.DialView
       android:id="@+id/dialView"
       android:layout_width="@dimen/fan_dimen"
       android:layout_height="@dimen/fan_dimen"
       app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       android:layout_marginLeft="@dimen/default_margin"
       android:layout_marginRight="@dimen/default_margin"
       android:layout_marginTop="@dimen/default_margin" />
  1. Run the app. Your fan control view appears in the activity. If you click it, nothing happens.